#include "Image.h"
#include <iostream>
#include <cmath>
#include <cstdio>
#include <cstdlib>
using namespace std;
Image::Image(int h, int w) :Matrix(h, w) {}
Image::Image(int h, int w, unsigned char val) : Matrix(h, w, val) {}
Image::Image(const char* ImageName)
{
	this->ReadBMP(ImageName);
}
Image::Image(unsigned char m[][100], int rows)
{
	Mat_he = rows;
	Mat_w = 100;
	Mat_da = new double* [Mat_he];

	for (int i = 0; i < Mat_he; i++)
	{
		Mat_da[i] = new double[Mat_w];
		for (int j = 0; j < Mat_w; j++)
		{
			Mat_da[i][j] = static_cast<unsigned char>(m[i][j]);
		}
	}
}
Image::Image(unsigned char** m, int h, int w)

{
	Mat_he = h;
	Mat_w = w;
	Mat_da = new double* [Mat_he];

	for (int i = 0; i < Mat_he; i++)
	{
		Mat_da[i] = new double[Mat_w];
		for (int j = 0; j < Mat_w; j++)
		{
			Mat_da[i][j] = static_cast<unsigned char>(m[i][j]);
		}
	}
}
Image::Image(const Matrix& m) :Matrix(m) {}
Image::Image(const Image& mat) : Matrix(mat) 
{
	bmpfileheader = mat.bmpfileheader;
	bitmapinfoheader = mat.bitmapinfoheader;
}
Image::~Image(){}
void Image::ReadBMP(const char* filename)
{
	FILE* fp;
	fopen_s(&fp, filename, "rb");
	if (!fp)
	{
		cout << "cannot open file";
		exit(0);

	}
	fread(&bmpfileheader, sizeof(bmpfileheader), 1, fp);
	fread(&bitmapinfoheader, sizeof(bitmapinfoheader), 1, fp);

	Mat_he = bitmapinfoheader.biHeight;
	Mat_w = bitmapinfoheader.biWidth;

	Mat_da = new double* [Mat_he];
	for (int i = 0; i < Mat_he; i++)
	{
		Mat_da[i] = new double[Mat_w];
		for (int j = 0; j < Mat_w; j++)
		{
			unsigned char tdb[3] = { 0 };
			fread(&tdb[0], 1, 1, fp);
			fread(&tdb[1], 1, 1, fp);
			fread(&tdb[2], 1, 1, fp);
			Mat_da[i][j] = (static_cast<double>(tdb[0] + tdb[1] + tdb[2])) / 3;
		}
	}

	fclose(fp);
}
void Image::WriteBMP(const char* filename)
{
	FILE* fp = NULL;
	fopen_s(&fp, filename, "wb");
	bool flag = false;
	while (Mat_w % 4)
	{
		Mat_w++;
	}

	bitmapinfoheader.biWidth = Mat_w;
	bitmapinfoheader.biHeight = Mat_he;
	bitmapinfoheader.biSizeImage = flag ?
		((((Mat_w * bitmapinfoheader.biBitCount) + 31) / 32 * 4) * Mat_he) : Mat_w * Mat_he;

	fwrite(&bmpfileheader, sizeof(bmpfileheader), 1, fp);
	fwrite(&bitmapinfoheader, sizeof(bitmapinfoheader), 1, fp);

	unsigned char t = 0;
	for (int i = 0; i < bitmapinfoheader.biHeight; i++)
	{
		for (int j = 0; j < bitmapinfoheader.biWidth; j++)
		{
			t = static_cast<unsigned char>(Mat_da[i][j]);
			fwrite(&t, 1, 1, fp);
			fwrite(&t, 1, 1, fp);
			fwrite(&t, 1, 1, fp);
		}
	}

	fclose(fp);
}
void Image::Flip(int code)
{
	double** t_data = new double* [Mat_he];
	for (int i = 0; i < Mat_he; i++)
	{
		t_data[i] = new double[Mat_w];
		for (int j = 0; j < Mat_w; j++)
		{
			t_data[i][j] = Mat_da[i][j];
		}
	}

	if (code)
	{
		for (int i = 0; i < Mat_he / 2; i++)
		{
			double* temp = t_data[i];
			t_data[i] = t_data[Mat_he - 1 - i];
			t_data[Mat_he - 1 - i] = temp;
		}
		myImageFree(Mat_da, Mat_he);
		Mat_da = t_data;
	}
	else
	{
		for (int i = 0; i < Mat_he; i++)
		{
			for (int j = 0; j < Mat_w / 2; j++)
			{
				double temp = t_data[i][j];
				t_data[i][j] = t_data[i][Mat_w - 1 - j];
				t_data[i][Mat_w - 1 - j] = temp;
			}
		}
		myImageFree(Mat_da, Mat_he);
		Mat_da = t_data;
	}
}

void Image::Resize(int h, int w)
{

	int oldw = Mat_w;
	int oldh = Mat_he;
	int neww = w;
	int newh = h;



	if (ceil(neww * 1.0 / oldw) <= 2)
	{
		if (ceil(newh * 1.0 / oldh) <= 2)
		{
			Resize(newh, neww, 1);
		}
		else
		{
			int cnt = newh / oldh;
			int temph = oldh;
			for (int i = 0; i < cnt - 1; i++)
			{
				temph += oldh;
				Resize(temph, neww, 1);
			}
			Resize(temph + (newh % oldh), neww, 1);
		}
	}
	else
	{
		if (ceil(newh * 1.0 / oldh) <= 2)
		{
			int cnt = neww / oldw;
			int tempw = oldw;
			for (int i = 0; i < cnt - 1; i++)
			{
				tempw += oldw;
				Resize(newh, tempw, 1);
			}
			Resize(newh, tempw + (neww % oldw), 1);
		}
		else
		{
			int cnt = newh / oldh;
			int temph = oldh;
			for (int i = 0; i < cnt - 1; i++)
			{
				temph += oldh;
				Resize(temph, oldw, 1);
			}
			Resize(temph + (newh % oldh), oldw, 1);

			cnt = neww / oldw;
			int tempw = oldw;
			for (int i = 0; i < cnt - 1; i++)
			{
				tempw += oldw;
				Resize(newh, tempw, 1);
			}
			Resize(newh, tempw + (neww % oldw), 1);
		}
	}
}
void Image::Resize(int h, int w, int)
{
	int oldh = h;
	int oldw = w;
	int width = Mat_w;
	int height = Mat_he;
	int dw = static_cast<int>(fabs(width - w));
	int dh = static_cast<int>(fabs(height - h));
	if (dw < 1)
	{
		dw = 1;
	}
	if (dh < 1)
	{
		dh = 1;
	}
	int maxw = w > width ? w : width;
	int maxh = h > height ? h : height;
	int difw = maxw / dw;
	int difh = maxh / dh;
	double** data = new double* [h];
	for (int i = 0, prii = 0; i < h; i++)
	{
		data[i] = new double[w];
		if ((i + 1) % difh == 0)
		{
			if (h < height)
			{
				prii++;
			}
			else
			{
				continue;
			}
		}
		for (int j = 0, prij = 0; j < w; j++)
		{
			if ((j + 1) % difw == 0)
			{
				if (w < width)
				{
					prij++;
				}
				else
				{
					continue;
				}
			}
			data[i][j] = Mat_da[prii][prij];
			if (prij < width - 1)
				prij++;
		}
		if (prii < height - 1)
			prii++;
	}
	if (w > width)
	{
		for (int i = 0; i < h; i++)
		{
			for (int j = 0; j < w; j++)
			{
				if ((j + 1) % difw == 0)
				{
					data[i][j] = data[i][j - 1];
				}
			}
		}
	}

	if (h > height)
	{
		for (int i = 0; i < h; i++)
		{
			if ((i + 1) % difh == 0)
			{
				for (int j = 0; j < w; j++)
				{
					data[i][j] = data[i - 1][j];
				}
			}
		}
	}
	myImageFree(Mat_da, Mat_he);
	Mat_da = data;
	Mat_he = h;
	Mat_w = w;
}

void Image::Cut(int x1, int y1, int x2, int y2)
{
	if (x2 > x1 && y2 > y1)
	{
		int h = y2 - y1 + 1;
		int w = x2 - x1 + 1;
		while (w % 4)
		{
			w++;
		}
		double** t_data = new double* [h];
		for (int i = y1, m = 0; i <= y2; i++, m++)
		{
			t_data[m] = new double[w];
			for (int j = x1, n = 0; j <= x2; j++, n++)
			{
				t_data[m][n] = Mat_da[i][j];
			}
		}

		myImageFree(Mat_da, h);
		Mat_da = t_data;
		Mat_he = h;
		Mat_w = w;
	}
	else
	{
		cout << "error" << endl;
	}
}
void Image::Rotate(int degree)
{
	if (degree == 90)
	{
		int temp = Mat_w;
		Mat_w = Mat_he;
		Mat_he = temp;
		double** data = new double* [Mat_he];
		for (int i = 0; i < Mat_he; i++)
		{
			data[i] = new double[Mat_w];
			for (int j = 0; j < Mat_w; j++)
			{
				data[i][j] = Mat_da[Mat_w - j - 1][i];
			}
		}

		myImageFree(Mat_da, Mat_he);
		Mat_da = data;
	}
	else if (degree == 180)
	{
		double** data = new double* [Mat_he];
		for (int i = 0; i < Mat_he; i++)
		{
			data[i] = new double[Mat_w];
			for (int j = 0; j < Mat_w; j++)
			{
				data[i][j] = Mat_da[Mat_he - i - 1][Mat_w - j - 1];
			}
		}
		myImageFree(Mat_da, Mat_he);
		Mat_da = data;
	}
	else if (degree == 270)
	{
		int temp = Mat_w;
		Mat_w = Mat_he;
		Mat_he = temp;
		double** data = new double* [Mat_he];
		for (int i = 0; i < Mat_he; i++)
		{
			data[i] = new double[Mat_w];
			for (int j = 0; j < Mat_w; j++)
			{
				data[i][j] = Mat_da[j][Mat_he - 1 - i];
			}
		}
		myImageFree(Mat_da, Mat_he);
		Mat_da = data;
	}
	else
	{
		cout << "error" << endl;
	}
}


double Image::Mean()
{
	double sum = 0;
	for (int i = 0; i < Mat_he; i++)
	{
		for (int j = 0; j < Mat_w; j++)
		{
			sum += Mat_da[i][j];
		}
	}
	return sum / (Mat_he * Mat_w);
}
double Image::Variance()
{
	double sum = 0;
	double ave = Mean();
	for (int i = 0; i < Mat_he; i++)
	{
		for (int j = 0; j < Mat_w; j++)
		{
			sum += (Mat_da[i][j] - ave) * (Mat_da[i][j] - ave);
		}
	}
	return sum / (Mat_he * Mat_w);
}


void Image::myImageFree(double** data, int h)
{
	if (data != NULL)
	{
		for (int i = 0; i < h; i++)
		{
			if (data[i] != NULL)
			{
				delete[] data[i];
				data[i] = NULL;
			}
		}
		delete[] data;
		data = NULL;
	}
}
void Image::showImage()
{
	cout << "height = " << Mat_he << endl;
	cout << "width = " << Mat_w << endl;
	for (int i = 0; i < 4; i++)
	{
		for (int j = 0; j < 4; j++)
		{
			cout << Mat_da[i][j] << " ";
		}
		cout << endl;
	}
	cout << endl;

}


